/*******************************************************************************
* Copyright (c) 2014 BestSolution.at and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Christoph Caks <ccaks@bestsolution.at> - initial API and implementation
*******************************************************************************/
package at.bestsolution.persistence.emap.generator;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.emf.codegen.ecore.genmodel.GenClass;
import org.eclipse.emf.codegen.ecore.genmodel.GenClassifier;
import org.eclipse.emf.codegen.ecore.genmodel.GenDataType;
import org.eclipse.emf.codegen.ecore.genmodel.GenEnum;
import org.eclipse.emf.codegen.ecore.genmodel.GenFeature;
import org.eclipse.emf.codegen.ecore.genmodel.GenModel;
import org.eclipse.emf.codegen.ecore.genmodel.GenPackage;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.plugin.EcorePlugin;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import at.bestsolution.persistence.emap.eMap.EType;
public class EClassLookupServiceImpl implements IEClassLookupService, IResourceChangeListener {
private static boolean debug = Boolean.getBoolean("emap.eclasslookup.verbose");
private ResourceSet masterResourceSet;
//
private Map<URI, GenModel> genModelCache = new WeakHashMap<URI, GenModel>();
private Map<String, GenPackage> genPackageCache = new WeakHashMap<String, GenPackage>();
// this cache allows us to immediate answer - this one makes it fast
private Map<String, EClass> eClassCache = new WeakHashMap<String, EClass>();
private Map<String, EDataType> eDataTypeCache = new WeakHashMap<String, EDataType>();
private Map<URI, URI> eCoreToGenModelMap = new HashMap<URI, URI>();
private void flushSpeedCaches() {
flushGenPackageCache();
flushEClassCache();
flushEDataTypeCache();
}
private void flushEClassCache() {
eClassCache.clear();
}
private void flushGenModelCache() {
genModelCache.clear();
// clear rs
masterResourceSet.getResources().clear();
}
private void flushEDataTypeCache() {
eDataTypeCache.clear();
}
private void flushGenPackageCache() {
genPackageCache.clear();
}
private void evictGenModel(URI uri) {
genModelCache.remove(uri);
}
@Override
public void resourceChanged(IResourceChangeEvent event) {
try {
event.getDelta().accept(new IResourceDeltaVisitor() {
@Override
public boolean visit(IResourceDelta delta) throws CoreException {
if ("genmodel".equals(delta.getFullPath().getFileExtension())) {
final URI uri = URI.createPlatformResourceURI(delta.getFullPath().toString(), false);
if (debug) System.err.println(" => " + uri);
// when a genmodel is changed, we evict it from our cache
// and flush the other caches
// TODO evictGenModel(uri);
flushGenModelCache();
flushSpeedCaches();
return false;
}
else if ("ecore".equals(delta.getFullPath().getFileExtension())) {
final URI ecoreURI = URI.createPlatformResourceURI(delta.getFullPath().toString(), false);
if (debug) System.err.println(" => " + ecoreURI);
final URI genmodelURI = eCoreToGenModelMap.get(ecoreURI);
// when a genmodel is changed, we evict it from our cache
// and flush the other caches
// TODO evictGenModel(genmodelURI);
flushGenModelCache();
flushSpeedCaches();
return false;
}
return true;
}
});
} catch (CoreException e) {
e.printStackTrace();
}
}
private String getFastId(String nsURI, String className) {
return nsURI + "#" + className;
}
private GenModel loadGenModel(URI uri) {
if (debug) System.err.println("loadGenModel! " + uri);
// final ResourceSet rs = new ResourceSetImpl();
Resource resource;
try {
resource = masterResourceSet.getResource(uri, true);
if( resource.getContents().isEmpty() ) {
throw new Exception();
}
} catch( Exception e ) {
System.err.println("Loading from resource");
// try in the target platform!
uri = URI.createURI(uri.toString().replaceFirst("resource", "plugin"));
resource = masterResourceSet.getResource(uri, true);
}
if (!resource.getContents().isEmpty()) {
final GenModel model = (GenModel) resource.getContents().get(0);
// model.reconcile();
// for (GenPackage gp : model.getGenPackages()) {
// fixAllInstanceClassNames(gp);
// }
genModelCache.put(uri, model);
return model;
}
else {
System.err.println("COULD NOT LOAD GENMODEL!!!!!! " + uri);
return null;
}
}
private GenModel getGenModel(URI uri) {
long l = System.currentTimeMillis();
try {
final GenModel result = genModelCache.get(uri);
if (result != null) {
if (debug) System.err.println("found genmodel in cache!");
return result;
}
return loadGenModel(uri);
}
finally {
long e = System.currentTimeMillis() - l;
if (debug) System.err.println("getGenModel needed " + e + "ms");
}
}
private GenModel getGenModel(String nsURI) {
return getGenModel(getGenmodelURI(nsURI));
}
private GenPackage findGenPackage(String nsURI) {
long l = System.currentTimeMillis();
try {
for (GenPackage p : getGenModel(nsURI).getAllGenPackagesWithClassifiers()) {
if (debug) System.err.println("checking " + p.getNSURI());
if (nsURI.equals(p.getNSURI())) {
URI ecoreURI = p.getEcoreModelElement().eResource().getURI();
eCoreToGenModelMap.put(ecoreURI, getGenmodelURI(nsURI));
genPackageCache.put(nsURI, p);
return p;
}
}
return null;
}
finally {
long e = System.currentTimeMillis() - l;
if (debug) System.err.println("findGenPackage needed " + e + "ms");
}
}
@Override
public GenPackage toGenModel(EPackage ePackage) {
return getGenPackage(ePackage.getNsURI());
}
private GenPackage getGenPackage(String nsURI) {
GenPackage cacheResult = genPackageCache.get(nsURI);
if (cacheResult != null) {
return cacheResult;
}
else {
return findGenPackage(nsURI);
}
}
private void fixInstanceClassName(GenClassifier genClassifier) {
if (genClassifier.getEcoreClassifier().getInstanceClassName() == null) {
if (debug) System.err.println("setting instanceClassName for " + genClassifier.getName() + " to " + genClassifier.getImportedInstanceClassName());
final String instanceClassName = genClassifier.getImportedInstanceClassName();
genClassifier.getEcoreClassifier().setInstanceClassName(instanceClassName);
// we cannot set the instanceClass
// try {
// eClassifier.setInstanceClass(Class.forName(instanceClassName));
// } catch (ClassNotFoundException e) {
// e.printStackTrace();
// }
if (genClassifier instanceof GenClass) {
GenClass genClass = (GenClass) genClassifier;
// we fix the attribute types
for (GenFeature genFeature : genClass.getChildrenFeatures()) {
GenClassifier genT = genFeature.getTypeGenClassifier();
fixInstanceClassName(genT);
}
// we fix the superclasses
for (GenClass superGenClass : genClass.getBaseGenClasses()) {
fixInstanceClassName(superGenClass);
}
}
}
}
private void fixAllInstanceClassNames(GenModel genModel) {
// if (debug) System.err.println("fixing instanceClassNames in " + genModel);
for (GenPackage gp : genModel.getGenPackages()) {
fixAllInstanceClassNames(gp);
}
}
private void fixAllInstanceClassNames(GenPackage gp) {
if (debug) System.err.println("fixing instanceClassNames in " + gp.getNSURI());
for (GenClass genClass : gp.getGenClasses()) {
fixInstanceClassName(genClass);
}
for (GenDataType genDataType : gp.getGenDataTypes()) {
fixInstanceClassName(genDataType);
}
for (GenEnum genEnum : gp.getGenEnums()) {
fixInstanceClassName(genEnum);
}
for (GenPackage subGenPackage : gp.getSubGenPackages()) {
fixAllInstanceClassNames(subGenPackage);
}
}
private EClass findEClass(GenPackage gp, String className) {
for( GenClass genClass : gp.getGenClasses() ) {
if (className.equals(genClass.getName())) {
// fixInstanceClassName(genClass);
return genClass.getEcoreClass();
}
}
// eclass does not exist in specified package
if (debug) System.err.println("eclass " + className + " does not exist in specified package " + gp.getNSURI());
return null;
}
private URI getGenmodelURI(String nsURI) {
Map<String, URI> map = EcorePlugin.getEPackageNsURIToGenModelLocationMap(true);
if (!map.containsKey(nsURI)) {
map = EcorePlugin.getEPackageNsURIToGenModelLocationMap(false);
}
URI r = map.get(nsURI);
if (debug) System.err.println(nsURI + " maps to " + r);
return r;
}
private EClass findEClass(String nsURI, String className) {
EClass result = null;
// first we try the registry
EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(nsURI);
if( ePackage != null ) {
result = (EClass) ePackage.getEClassifier(className);
}
// then we try our genmodel search
if (result == null) {
result = findEClass(getGenPackage(nsURI), className);
}
// add it to fastcache
if (result != null) {
eClassCache.put(getFastId(nsURI, className), result);
}
else {
if (debug) System.err.println("=> NO RESULT! " + nsURI + " # " + className);
}
return result;
}
private EDataType findEDataType(GenPackage gp, String className) {
for( GenDataType genDataType : gp.getGenDataTypes() ) {
if( className.equals(genDataType.getName()) ) {
// fixInstanceClassName(genDataType);
return genDataType.getEcoreDataType();
}
}
// edatatype does not exist in specified package
if (debug) System.err.println("edatatype " + className + " does not exist in specified package " + gp.getNSURI());
return null;
}
private EDataType findEDataType(String nsURI, String className) {
EDataType result = null;
// first we try the registry
EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(nsURI);
if( ePackage != null ) {
result = (EDataType) ePackage.getEClassifier(className);
}
// then we try our genmodel search
if (result == null) {
result = findEDataType(getGenPackage(nsURI), className);
}
// add it to fastcache
if (result != null) {
eDataTypeCache.put(getFastId(nsURI, className), result);
}
else {
if (debug) System.err.println("=> NO RESULT! " + nsURI + " # " + className);
}
return result;
}
private EDataType getEDataType(String nsURI, String className) {
final String fastId = getFastId(nsURI, className);
final EDataType fastResult = eDataTypeCache.get(fastId);
if (fastResult != null) {
return fastResult;
}
return findEDataType(nsURI, className);
}
private EClass getEClass(String nsURI, String className) {
final String fastId = getFastId(nsURI, className);
final EClass fastResult = eClassCache.get(fastId);
if (fastResult != null) {
return fastResult;
}
return findEClass(nsURI, className);
}
public void activate() {
masterResourceSet = new ResourceSetImpl();
masterResourceSet.eAdapters().add(new Adapter() {
@Override
public void notifyChanged(Notification notification) {
if (notification.getNotifier() instanceof ResourceSet) {
if (notification.getFeatureID(ResourceSet.class) == ResourceSet.RESOURCE_SET__RESOURCES && notification.getEventType() == Notification.ADD) {
Resource resource = ((ResourceSet)notification.getNotifier()).getResources().get(notification.getPosition());
if (resource != null) {
// adding ourself as adapter on the resource
resource.eAdapters().add(this);
}
}
}
else if (notification.getNotifier() instanceof Resource) {
final Resource resource = (Resource)notification.getNotifier();
if (notification.getFeatureID(Resource.class) == Resource.RESOURCE__IS_LOADED && notification.getNewBooleanValue() == true) {
final GenModel genModel = (GenModel) resource.getContents().get(0);
if (debug) System.err.println("fixing model: " + genModel);
genModel.reconcile();
fixAllInstanceClassNames(genModel);
}
}
}
@Override
public Notifier getTarget() {
return null;
}
@Override
public void setTarget(Notifier newTarget) {
}
@Override
public boolean isAdapterForType(Object type) {
System.err.println("masterResourceSet: isAdapterForType: " + type);
return false;
}
});
ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
}
public void deactivate() {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
}
@Override
public EClass getEClass(EType type) {
try {
final EClass result = getEClass(type.getUrl(), type.getName());
if (result.getInstanceClassName() == null) {
System.err.println("instanceClassName was not set " + result);
new Exception().printStackTrace();
}
return result;
}
catch (Throwable x) {
x.printStackTrace();
System.err.println("Exception occoured! Result is null");
return null;
}
}
@Override
public EDataType getEDataType(EType type) {
try {
EDataType result = getEDataType(type.getUrl(), type.getName());
if (result.getInstanceClassName() == null) {
System.err.println("instanceClassName was not set " + result);
new Exception().printStackTrace();
}
return result;
}
catch (Throwable x) {
x.printStackTrace();
System.err.println("Exception occoured! Result is null");
return null;
}
}
@Override
public String getFeatureClassifier(EStructuralFeature f) {
EClass eClass = f.getEContainingClass();
GenPackage pack = getGenPackage(eClass.getEPackage().getNsURI());
String rv = null;
for( GenClassifier gc : pack.getGenClassifiers() ) {
if( gc.getName().equals(eClass.getName()) ) {
if( gc instanceof GenClass ) {
GenClass gec = (GenClass) gc;
for( GenFeature gf : gec.getAllGenFeatures() ) {
if( gf.getName().equals(f.getName()) ) {
rv = gec.getFeatureID(gf);
break;
}
}
}
}
}
return rv;
}
}